Golang : GUI with Qt and OpenCV to capture image from camera
After the tutorial on how to display image with Qt and Golang. I need to build a Graphical User Interface(GUI) application with OpenCV and Qt to capture images from a camera. It is kinda similar to Apple's PhotoBooth application, but a less sophisticated version.
The Golang's OpenCV binding has a highgui package that allows GUI application creation, but lacking of buttons to customise further action - such as to trigger a save to file action or select different filtering algorithms. Sure, I can always build a web-based interface for OpenCV such as this surveillance tutorial, but I'm itching to learn some coding with Qt GUI framework.
The code that follows is my own attempt to create a GUI application to bring up the camera feed into a GUI application with OpenCV, have a slider to control the filter, a button to take a snapshot and finally a button to exit the GUI application.
NOTES:
By all means, I'm not an expert in creating Qt GUI(Graphical User Interface) application, so pardon me if you spot some silly methods in the code. The recommended way of interfacing with a camera using Qt is documented here(C++ source code) - http://doc.qt.io/qt-5/qtmultimedia-multimediawidgets-camera-example.html. For this example and the sake of simplicity, we will use OpenCV to interface with the camera and a goroutine to update the images.
!! Please follow up the setup instruction at https://github.com/therecipe/qt to setup Qt. !!
Here you go!
package main
import (
"bytes"
"fmt"
"github.com/lazywei/go-opencv/opencv"
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/widgets"
"image/jpeg"
"os"
"strings"
)
var (
positionsSlider *widgets.QSlider
sliderPos int
displayArea *widgets.QWidget
scene *widgets.QGraphicsScene
view *widgets.QGraphicsView
item *widgets.QGraphicsPixmapItem
mainApp *widgets.QApplication
snapshotFileName string
webCamera = new(opencv.Capture)
qImg = new(gui.QImage)
cameraFeedLabel *widgets.QLabel
opt = jpeg.Options{90} // for the best color, use png instead of jpeg
stopCamera = false // to prevent segmentation fault
snapPhoto = false
width, height int
)
// credits to https://github.com/lazywei/go-opencv/blob/master/samples/webcam.go
func applyFilters(img *opencv.IplImage) opencv.IplImage {
w := img.Width()
h := img.Height()
// Create the output image
cedge := opencv.CreateImage(w, h, opencv.IPL_DEPTH_8U, 3)
defer cedge.Release()
// Convert to grayscale
gray := opencv.CreateImage(w, h, opencv.IPL_DEPTH_8U, 1)
edge := opencv.CreateImage(w, h, opencv.IPL_DEPTH_8U, 1)
defer gray.Release()
defer edge.Release()
opencv.CvtColor(img, gray, opencv.CV_BGR2GRAY)
opencv.Smooth(gray, edge, opencv.CV_BLUR, 3, 3, 0, 0)
opencv.Not(gray, edge)
// Run the edge detector on grayscale
opencv.Canny(gray, edge, float64(sliderPos), float64(sliderPos*3), 3)
opencv.Zero(cedge)
// copy edge points
opencv.Copy(img, cedge, edge)
return *cedge
}
func processFrameAndUpdate() {
for {
if webCamera.GrabFrame() && !stopCamera {
IplImgFrame := webCamera.RetrieveFrame(1)
if IplImgFrame != nil {
// Have to make the image frame
// smaller, so that it does not blow up the GUI application.
IplImgFrame = opencv.Resize(IplImgFrame, 0, height-200, opencv.CV_INTER_LINEAR)
// apply filters
*IplImgFrame = applyFilters(IplImgFrame)
//applyFilters(IplImgFrame)
// snap photo
if snapPhoto {
fmt.Println("Snapshot save to : ", snapshotFileName)
// params - to set jpeg quality
// but unable to pass params []int slice to opencv...
opencv.SaveImage(snapshotFileName, IplImgFrame, opencv.CV_IMWRITE_JPEG_QUALITY)
snapPhoto = false // reset to false again
}
// slower than the openCV's showImage()
// simply because we need to convert to QImage
goImgFrame := IplImgFrame.ToImage()
frameBuffer := new(bytes.Buffer)
err := jpeg.Encode(frameBuffer, goImgFrame, &opt)
if err != nil {
panic(err)
}
qImg := gui.QImage_FromData2(frameBuffer.String(), "")
cameraFeedLabel.SetPixmap(gui.QPixmap_FromImage(qImg, core.Qt__AutoColor))
}
}
}
}
func setPosition(position int) {
sliderPos = position
}
func mainGUI() *widgets.QWidget {
scene = widgets.NewQGraphicsScene(nil)
view = widgets.NewQGraphicsView(nil)
displayArea = widgets.NewQWidget(nil, 0)
cameraFeedLabel = widgets.NewQLabel(nil, core.Qt__Widget)
// use the initial frame to populate the layout
if webCamera.GrabFrame() {
IplImgFrame := webCamera.RetrieveFrame(1)
if IplImgFrame != nil {
// Have to make the image frame
// smaller, so that it does not blow up the GUI application.
IplImgFrame = opencv.Resize(IplImgFrame, 0, height-200, opencv.CV_INTER_LINEAR)
// convert IplImage(Intel Image Processing Library)
// to QImage via image.Image->[]byte->QPixmap->QLabel
// I'm pretty sure there are more efficient method out there
// but I'm just too lazy to find out the number of channels, blah blah...
// and also at the time of writing ... go-opencv does not have CV_BGR2RGB :(
// such as this C++ code from
// https://decibel.ni.com/content/blogs/kl3m3n/2015/06/09/opencv-and-qt-based-gui-hough-circle-detection-example
// cv::cvtColor(original,original,CV_BGR2RGB);
// QImage qimgOriginal((uchar*) original.data,original.cols,original.rows,original.step,QImage::Format_RGB888); // for color images
// QImage qimgProcessed((uchar*) processed.data,processed.cols,processed.rows,processed.step,QImage::Format_Indexed8); // for grayscale images
// or
// QImage qt_img = ( QImage ( cv_img->dataIm, cv_img->width, cv_img->height, cv_img->QImage::Format_RGB888 ) ).rgbSwapped();
goImgFrame := IplImgFrame.ToImage()
// and then convert to []byte
// with the help of jpeg.Encode() function
frameBuffer := new(bytes.Buffer)
err := jpeg.Encode(frameBuffer, goImgFrame, &opt)
if err != nil {
panic(err)
}
qImg := gui.QImage_FromData2(frameBuffer.String(), "")
cameraFeedLabel.SetPixmap(gui.QPixmap_FromImage(qImg, core.Qt__AutoColor))
scene.AddWidget(cameraFeedLabel, core.Qt__Widget)
// The proper way to do a Qt GUI application for camera is to use
// QtMultimedia widgets, but for simplicity sake,
// let's do the "traditional" openCV way for this example.
}
}
view.SetScene(scene)
// create a slider
positionsSlider = widgets.NewQSlider2(core.Qt__Horizontal, nil)
positionsSlider.SetRange(0, 100)
positionsSlider.SetTickInterval(10)
positionsSlider.SetValue(50)
positionsSlider.ShowMaximized()
positionsSlider.ConnectSliderMoved(setPosition)
//create a button and connect the clicked signal
var snapButton = widgets.NewQPushButton2("Take a snapshot", nil)
snapButton.ConnectClicked(func(flag bool) {
snapPhoto = true
})
//create a button and connect the clicked signal
var quitButton = widgets.NewQPushButton2("Quit", nil)
quitButton.ConnectClicked(func(flag bool) {
stopCamera = true
webCamera.Release() // don't forget to release !!
mainApp.Quit()
})
var layout = widgets.NewQVBoxLayout()
layout.AddWidget(view, 0, core.Qt__AlignCenter)
layout.AddWidget(positionsSlider, 0, core.Qt__AlignCenter)
layout.AddWidget(snapButton, 0, core.Qt__AlignCenter)
layout.AddWidget(quitButton, 0, core.Qt__AlignRight)
displayArea.SetLayout(layout)
return displayArea
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage : %s <save snapshot filename>\n", os.Args[0])
os.Exit(0)
}
snapshotFileName = os.Args[1]
if !strings.HasSuffix(snapshotFileName, ".jpg") {
snapshotFileName += ".jpg"
}
fmt.Println("Will be saving snapshot image to : ", snapshotFileName)
// warming up our camera
webCamera = opencv.NewCameraCapture(0)
if webCamera == nil {
panic("Unable to open camera")
}
// get some data from camera
width = int(webCamera.GetProperty(opencv.CV_CAP_PROP_FRAME_WIDTH))
height = int(webCamera.GetProperty(opencv.CV_CAP_PROP_FRAME_HEIGHT))
fmt.Println("Camera width : ", width)
fmt.Println("Camera height : ", height)
mainApp = widgets.NewQApplication(len(os.Args), os.Args)
mainGUI().SetWindowTitle("Golang Qt OpenCV GUI application example")
mainGUI().SetWindowState(core.Qt__WindowMaximized) // maximized on start
mainGUI().Show()
go processFrameAndUpdate() // goroutine to update feed from camera
widgets.QApplication_Exec()
}
Further reading ---> http://blog.qt.io/blog/2015/03/20/introducing-video-filters-in-qt-multimedia/
References:
https://github.com/lazywei/go-opencv/blob/master/samples/webcam.go
http://stackoverflow.com/questions/11543298/qt-opencv-displaying-images-on-qlabel
https://github.com/therecipe/qt/blob/master/internal/examples/widgets/videoplayer/videoplayer.go
https://github.com/therecipe/qt
https://www.socketloop.com/tutorials/golang-edge-detection-with-sobel-method
See also : Golang : Surveillance with web camera and OpenCV
By Adam Ng
IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.
Advertisement
Tutorials
+8.5k Golang : Take screen shot of browser with JQuery example
+12.6k Python : Convert IPv6 address to decimal and back to IPv6
+6.7k Golang : Levenshtein distance example
+9.5k Golang : Load ASN1 encoded DSA public key PEM file example
+30.9k Golang : bufio.NewReader.ReadLine to read file line by line
+8.8k Golang : Gonum standard normal random numbers example
+8.8k nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
+6k Golang : Extract sub-strings
+10.8k Golang : Roll the dice example
+4.5k Chrome : How to block socketloop.com links in Google SERP?
+11k Golang : Fix fmt.Scanf() on Windows will scan input twice problem
+6k Golang : How to get capacity of a slice or array?